package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.model.search.vos.SearchArticleVo;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.ArticleSearch;
import com.heima.utils.common.ApThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ArticleSearchImpl implements ArticleSearch {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private ApUserSearchService apUserSearchService;

    /**
     * 文章搜索
     * @param dto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto dto) throws IOException {
        if(StringUtils.isBlank(dto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"搜索关键词不能为空");
        }
        // 创建搜索请求对象，并且指定索引库
        SearchRequest searchRequest = new SearchRequest("app_info_article");
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.matchQuery("all", dto.getSearchWords()));
        // 时间条件判断
        if(dto.getMinBehotTime() != null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lte(dto.getMinBehotTime()));
        }
        // 准备DSL
        searchRequest.source().query(boolQueryBuilder);
        // 分页
        searchRequest.source().from(0).size(dto.getPageSize());
        // 排序
        searchRequest.source().sort("publishTime", SortOrder.DESC);
        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").requireFieldMatch(false);
        highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        searchRequest.source().highlighter(highlightBuilder);
        // 执行搜索
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 解析结果
        ResponseResult responseResult = destroySearch(response);
        // 异步调用保存记录的接口
        if(ApThreadLocalUtil.getApUser() != null){
            Integer userId = ApThreadLocalUtil.getApUser().getId();
            apUserSearchService.insert(dto.getSearchWords(), userId);
        }

        return responseResult;
    }

    /**
     * 解析查询到的数据
     * @param response
     * @return
     */
    private ResponseResult destroySearch(SearchResponse response){
        List<Map> arrayList = new ArrayList<>();
        SearchHit[] hits = response.getHits().getHits(); // 获取命中的结果
        for (SearchHit hit : hits) {
            // 获取查询结果，返回是map集合
            Map<String, Object> map = hit.getSourceAsMap();
            // 判断是否有高亮值
            if(hit.getHighlightFields().size() > 0){
                // 有值,就取出高亮数组中的第一个
                Text[] titles = hit.getHighlightFields().get("title").fragments();
                if(titles.length > 0){
                    map.put("h_title",titles[0].toString());
                }
            }else{
                map.put("h_title",map.get("title"));
            }
            // 把结果封装到list
            arrayList.add(map);
        }


        return  ResponseResult.okResult(arrayList);
    }

    /**
     * 添加
     * @param searchArticleVo
     */
    public void custorm(SearchArticleVo searchArticleVo) {
        try {
            // 指定索引库
            IndexRequest request = new IndexRequest("app_info_article").id(searchArticleVo.getId().toString());
            String jsonString = JSON.toJSONString(searchArticleVo);
            request.source(jsonString, XContentType.JSON);
            // 添加到es中
            restHighLevelClient.index(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 自动补全
     *
     * @param userSearchDto
     */
    @Override
    public ResponseResult suggestion(UserSearchDto userSearchDto) {
        List<String> arrayList = new ArrayList<>();
        try {
            // 连接索引库
            SearchRequest searchRequest = new SearchRequest("app_info_article");
            // 自动补全设置
            searchRequest.source().suggest(
                    new SuggestBuilder().addSuggestion("mySuggestion", SuggestBuilders.completionSuggestion("suggestion")
                            .prefix(userSearchDto.getSearchWords()) // 要搜索的关键词
                            .skipDuplicates(true) // 去重
                            .size(userSearchDto.getPageSize())) // 查询的条数
            );
            // 执行查询
            SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            Suggest suggest = search.getSuggest();
            CompletionSuggestion mySuggestion = suggest.getSuggestion("mySuggestion");
            List<CompletionSuggestion.Entry.Option> options = mySuggestion.getOptions();
            for (CompletionSuggestion.Entry.Option option : options) {
                String s = option.getText().toString();
                arrayList.add(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return ResponseResult.okResult(arrayList);
    }
}
